package com.czy.util.json;

import com.czy.enums.IEnum;
import com.czy.util.ClassUtil;
import com.czy.util.OP;
import com.czy.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.commons.lang3.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;


/**
 * Jackson对json数据的操作
 *
 * @author chenzy
 * @since 2019.12.19
 */
public class JsonUtil {
    private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class);
    private static final SimpleModule simpleModule = new SimpleModule();
    private static ObjectMapper objectMapper = getObjectMapper();

    public static ObjectMapper createObjectMapper() {
        var objectMapper = new ObjectMapper();
        /*忽略null字段*/
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        /*允许json属性名不使用双引号*/
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        /*忽略不存在字段*/
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//        /*允许空字符串作为null*/
//        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,true);
//        /*允许单独字符串映射为数组*/
        objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

        /*反序列化：string=>对象*/
        simpleModule
                .addDeserializer(Boolean.class, new DeserializerStr2Bool())
                .addDeserializer(Date.class, new DeserializerStr2Date())
                .addDeserializer(IEnum.class, new DeserializerStr2IEnum<>())
                .addDeserializer(LocalDate.class, new DeserializerStr2LocalDate())
                .addDeserializer(LocalDateTime.class, new DeserializerStr2LocalDateTime())
                /*序列化：对象=>string*/
                .addSerializer(Boolean.class, new SerializerBool2Int())
                .addSerializer(Date.class, new SerializerDate2Str())
                .addSerializer(IEnum.class, new SerializerIEnum2T())
                .addSerializer(LocalDate.class, new SerializerLocalDate2Str())
                .addSerializer(LocalDateTime.class, new SerializerLocalDateTime2Str())
                .addSerializer(Timestamp.class, new SerializerTimestamp2LocalDateTime())
        ;
        /*去除首尾空格*/
        simpleModule.addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
            @Override
            public String deserialize(JsonParser p, DeserializationContext ctxt)
                    throws IOException {
                return p.getText().trim();
            }
        });
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }

    public static SimpleModule getSimpleModule() {
        return simpleModule;
    }

    public static ObjectMapper getObjectMapper() {
        if (objectMapper == null) {
            objectMapper = createObjectMapper();
        }
        return objectMapper;
    }

    /**
     * 对象=>json字符串=>另一个对象
     */
    public static <S, Target> OP<Target> model2Model(S model, Class<Target> targetClass) {
        if (model == null || targetClass == null) {
            return OP.empty();
        }
        //noinspection unchecked
        var srcClass = (Class<S>) model.getClass();
        //对象是Target类型或子类，
        if (ClassUtils.isAssignable(srcClass, targetClass)) {
            return OP.of(targetClass.cast(model));
        }
        if (model instanceof String) {
            try {
                return OP.ofNullable(objectMapper.readValue((String) model, targetClass));
            } catch (JsonProcessingException e) {
                logger.error("对象转换异常:", e);
                return OP.empty();
            }
        }
        return OP.ofNullable(objectMapper.convertValue(model, targetClass));
    }

    /**
     * 对象的字段是泛型时也能转换
     *
     * @param <T1> 对象类型
     * @param <T2> 字段的泛型
     */
    public static <S, T1, T2> OP<T1> model2Model(S model, Class<T1> tClass1, Class<T2> tClass2) {
        if (model == null || tClass1 == null || tClass2 == null) {
            return OP.empty();
        }
        return OP.ofNullable(objectMapper.convertValue(model, objectMapper.getTypeFactory().constructParametricType(tClass1, tClass2)));
    }

    public static <S, R> List<R> model2List(S model, Class<R> rClass) {
        return objectMapper.convertValue(model, objectMapper.getTypeFactory().constructParametricType(List.class, rClass));
    }

    public static <T> OP<T> str2Model(String json, TypeReference<T> tTypeReference) {
        if (ObjectUtil.isEmptyOr(json, tTypeReference)) {
            return OP.empty();
        }
        try {
            return OP.ofNullable(objectMapper.readValue(json, tTypeReference));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }

    public static OP<JsonNode> str2JsonNode(String jsonStr) {
        if (jsonStr == null) {
            return OP.empty();
        }
        try {
            return OP.of(objectMapper.readTree(jsonStr));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }

    public static <T1, T2> OP<T1> stream2Model(InputStream stream, Class<T1> tClass1, Class<T2> tClass2) {
        try {
            return OP.ofNullable(objectMapper.readValue(stream, objectMapper.getTypeFactory().constructParametricType(tClass1, tClass2)));
        } catch (IOException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        } finally {
            try {
                stream.close();
            } catch (IOException e) {
                //noinspection ReturnInsideFinallyBlock
                return OP.empty();
            }
        }
    }

    public static <T> OP<T> stream2Model(InputStream stream, Type type) {
        try {
            return OP.ofNullable(objectMapper.readValue(stream, new TypeReference<>() {
                @Override
                public Type getType() {
                    return type;
                }
            }));
        } catch (IOException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        } finally {
            try {
                stream.close();
            } catch (IOException e) {
                //noinspection ReturnInsideFinallyBlock
                return OP.empty();
            }
        }
    }

    /**
     * json字符转model的简单实现，适合简单的标准json字符
     * @noinspection unchecked
     */
    public static <T> OP<T> str2Model(String jsonStr, Class<T> modelClass) {
        if (ObjectUtil.isEmptyOr(jsonStr, modelClass)) {
            return OP.empty();
        }
        if (modelClass == String.class) {
            return (OP<T>) OP.ofNullable(jsonStr);
        }
        try {
            return OP.ofNullable(objectMapper.readValue(jsonStr, modelClass));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }

    /**
     * 对象的字段是其他对象时也能转换
     *
     * @param <T> 对象类型
     */
    public static <T> OP<T> str2Model(String jsonStr, Class<T> modelClass1, Class<?>... mchildMdelClass) {
        if (ObjectUtil.isEmptyOr(jsonStr, modelClass1)) {
            return OP.empty();
        }
        try {
            var javaType = objectMapper.getTypeFactory().constructParametricType(modelClass1, mchildMdelClass);
            return OP.ofNullable(objectMapper.readValue(jsonStr, javaType));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }

    public static <T> JavaType getJavaType(Class<T> modelClass1, Class<?>... mchildMdelClass) {
        return objectMapper.getTypeFactory().constructParametricType(modelClass1, mchildMdelClass);
    }

    /**
     * 对象的字段是其他对象时也能转换
     *
     * @param <T> 对象类型
     */
    public static <T> OP<T> str2Model(String jsonStr, JavaType javaType) {
        try {
            return OP.ofNullable(objectMapper.readValue(jsonStr, javaType));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }


    /**
     * 对象=>json字符串
     */
    public static <S> String model2Str(S model, String defaultValue) {
        if (model == null) {
            return defaultValue;
        }
        if (ClassUtil.isBasicType(model.getClass())) {
            return defaultValue;
        }
        try {
            return objectMapper.writeValueAsString(model);
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return defaultValue;
        }
    }

    /**
     * 对象=>json字符串
     */
    public static <S> OP<String> model2Str(S model) {
        if (model == null) {
            return OP.empty();
        }
        if (ClassUtil.isBasicType(model.getClass())) {
            return OP.of(model.toString());
        }
        try {
            return OP.of(objectMapper.writeValueAsString(model));
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return OP.empty();
        }
    }

    public static <S> String model2StrDefault(S model, String defaultStr) {
        if (model == null) {
            return defaultStr;
        }
        if (ClassUtil.isBasicType(model.getClass())) {
            return model.toString();
        }
        try {
            return objectMapper.writeValueAsString(model);
        } catch (JsonProcessingException e) {
            logger.error("对象转换异常:", e);
            return defaultStr;
        }
    }

}
